home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gekkan Dennou Club 142
/
Gekkan Dennou Club - 2000.3 Vol. 142 (Japan).7z
/
Gekkan Dennou Club - 2000.3 Vol. 142 (Japan) (Track 1).bin
/
ikap
/
etc2
/
src.lzh
/
sound.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-02-04
|
12KB
|
519 lines
/* sound.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mbctype.h>
#include <sys/dos.h>
#include <sys/xglob.h>
#include "tr68k.h"
#include "sound.h"
#include "pcm8call.h"
#include "GetPcmPath.h"
static void *pcm_ptr[TYPE_MAX][TRACK_MAX]; /* 読み込んだ .PCM データへのポインタ */
static char pcm_full_fname[TYPE_MAX][TRACK_MAX][92]; /* .CNF に書かれていたそのままの .PCM ファイル名 */
static struct {
int note_no[TRACK_MAX]; /* 次に書き込む note_no */
int l_current[TRACK_MAX]; /* 'l' で指定された現在の音長 */
int volume_current[TRACK_MAX]; /* 現在の音量 */
} zms_analyse;
/* .ZMS ファイル書き込み */
/* 綺麗な.ZMSを生成しようとしたらソースが泥沼になったにょ */
int SoundSaveZms (char *fname)
{
FILE *fp;
char note_char[] = "cdef";
int track, type;
if (fname == NULL)
fp = fopen ("TEMP.ZMS", "w");
else
fp = fopen (fname, "w");
if (fp == NULL)
return (-1);
fprintf (fp, ".comment TR-68K\n(i)\n"
"(m9,2048)(aADPCM,9)(m10,2048)(aADPCM,10)(m11,2048)(aADPCM,11)(m12,2048)(aADPCM,12)\n"
"(m13,2048)(aADPCM,13)(m14,2048)(aADPCM,14)(m15,2048)(aADPCM,15)(m16,2048)(aADPCM,16)\n\n"
);
/* .o0c = fname.pcm */
{
int pcm_use[TYPE_MAX][TRACK_MAX]; /* .PCM が実際に使用されているか */
int note_abs;
/* 初期化 */
for (track = 0; track < TRACK_MAX; track++) {
for (type = 0; type < TYPE_MAX; type++) {
pcm_use[type][track] = 0;
}
}
/* 実際に使用されている .PCM を探す */
for (track = 0; track < TRACK_MAX; track++) {
for (note_abs = 0; note_abs < bar_max_table[bar_max] * NOTE_DISP; note_abs++) {
if (note_volume[track][note_abs])
pcm_use[note_type[track][note_abs]][track] = !0;
}
}
/* .o0c = fname.pcm を書き込む */
for (track = 0; track < TRACK_MAX; track++) {
for (type = 0; type < TYPE_MAX; type++) {
if ((pcm_ptr[type][track]) && (pcm_use[type][track]))
fprintf (fp, ".o%d%c = %s\n", track, note_char[type], &pcm_full_fname[type][track][0]);
}
}
}
/* テンポを書き込む */
fprintf (fp, "\n(t9)\tt%d\n\n", VDISP60SEC / 4 / note_tempo);
/* トラックごとに演奏データを書き込む */
for (track = 0; track < TRACK_MAX; track++) {
char volume, volume_current;
int bar_count, note_count, note_count2; /* 空白や改行を入れるためのカウンタ */
int note_abs;
fprintf (fp, "(t%d)\to%d l16 v8 @d1\n\(t%d)\t[do]\n", track + 9, track, track + 9);
volume_current = 8;
bar_count = 0;
note_count = note_count2 = 0;
fprintf (fp, "(t%d)\t\t", track + 9);
for (note_abs = 0; note_abs < bar_max_table[bar_max] * NOTE_DISP; note_abs++) {
volume = note_volume[track][note_abs];
if (volume) { /* 音符がある */
#ifdef OPTIMISE_LENGTH
int pcm_length;
int i;
#endif
if (volume != volume_current) {
/* 前回との音量差によって分岐 */
switch (volume - volume_current) {
case 2:
fputs ("~~", fp);
break;
case 1:
fputs ("~", fp);
break;
case -1:
fputs ("_", fp);
break;
case -2:
fputs ("__", fp);
break;
default:
fprintf (fp, "v%d", volume);
break;
}
volume_current = volume;
}
fprintf (fp, "%c", note_char[note_type[track][note_abs]]);
#ifdef OPTIMISE_LENGTH
/* 16分音符の.PCMサイズ=7800/55*note_tempo */
pcm_length = pcm_size[note_type[track][note_abs]][track] / (7800 / 55 * note_tempo);
if (pcm_length > 3) /* 4分音符以上は鳴らせない */
pcm_length = 3;
/* .PCM の長さが16分音符何個分かで分岐 */
/* 8分音符なら「次が休符」である事を調べて'8'を出力とか */
/* これによって "cr" でなく "c8" の様に出力できる */
for (i = 0; i < pcm_length; i++) {
if ((note_abs + i) >= (bar_max_table[bar_max] * NOTE_DISP - 1))
break;
if (note_volume[track][note_abs + i + 1] != 0)
break;
}
switch (i) {
case 0:
break;
case 1:
fputs ("8", fp);
break;
case 2:
fputs ("8.", fp);
break;
case 3:
fputs ("4", fp);
break;
}
note_abs += i;
note_count += i;
#endif
} else {
fputs ("r", fp);
}
note_count++;
if (note_count >= 4) {
note_count -= 4;
fputs (" ", fp); /* 16分音符4つごとに空白を入れる */
note_count2++;
if (note_count2 >= 4) {
note_count2 -= 4;
bar_count++;
if (bar_count >= 2) {
bar_count = 0;
/* 2小節ごとに改行を入れる(最終小節でなければ) */
if (note_abs < bar_max_table[bar_max] * NOTE_DISP - 1)
fprintf (fp, "\n(t%d)\t\t", track + 9);
} else {
fputs (" ", fp); /* 4分音符4つごとに空白を入れる */
}
}
}
}
if (volume_current != 8)
fprintf (fp, "v8");
fprintf (fp, "\n(t%d)\t[loop]\n\n", track + 9);
}
fprintf (fp, "\n(p)\n");
fclose (fp);
return (0);
}
/*
.ZMS 解析:"(t9)" の場合
line_buffer に1行ぶんの .ZMS が入っているので、ヌル文字が現れるまで解析して
note_type[][], note_volume[][] に書き込む
*/
static void LoadZmsTrack (char *line_buffer, int track, int ct)
{
char *p = line_buffer + ct;
unsigned char c;
int i;
/* (t9) 行の行解析・桁ループ */
while (c = *p++) {
int eol_flag = 0; /* 行解析終わり */
if (isspace (c))
continue; /* 空白は読み飛ばす */
if (zms_analyse.note_no[track] >= BAR_MAX * NOTE_DISP)
break; /* バッファがいっぱい */
switch (c) {
case '/':
eol_flag = !0; /* '/' が現れたら1行解析終わり */
break;
case '[':
while ((c = *p++) && (c != ']')); /* ']' まで読み飛ばす */
if (!c)
eol_flag = !0; /* 行末に達した */
break;
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'a':
case 'b':
note_volume[track][zms_analyse.note_no[track]]
= zms_analyse.volume_current[track];
note_type[track][zms_analyse.note_no[track]] = c - 'c';
/* ここに break がない事に注意 */
case 'r':
/* 音階・休符の後の音長指定・16/8/8./4 のみ指定可能(弱い…) */
switch (*p) {
case '1':
if (*(p + 1) == '6') {
zms_analyse.note_no[track]++; /* 16 */
p += 2; /* "16" の次を指すように */
}
break;
case '8':
if (*(p + 1) == '.') {
zms_analyse.note_no[track] += 3; /* 8. */
p += 2; /* "8." の次を指すように */
} else {
zms_analyse.note_no[track] += 2; /* 8 */
p++; /* "8" の次を指すように */
}
break;
case '4':
zms_analyse.note_no[track] += 4; /* 4 */
p++; /* "4" の次を指すように */
break;
default:
zms_analyse.note_no[track]++; /* 16 */
break;
}
break;
case '~':
if (zms_analyse.volume_current[track] < 15)
zms_analyse.volume_current[track]++;
break;
case '_':
if (zms_analyse.volume_current[track] > 0)
zms_analyse.volume_current[track]--;
break;
case 'v':
i = 0;
while (isdigit (c = *p++)) {
i *= 10;
i += (c - '0');
}
p--; /* 行きすぎたぶん */
if ((i >= 0) && (i < 16))
zms_analyse.volume_current[track] = i;
break;
case 't': /* テンポ変更 */
i = 0;
while (isdigit (c = *p++)) {
i *= 10;
i += (c - '0');
}
p--; /* 行きすぎたぶん */
note_tempo = 1;
while (i < VDISP60SEC / 4 / note_tempo)
note_tempo++;
break;
case '@':
p++; /* @d などの d を読み飛ばす */
while (isdigit (c = *p++)); /* パラメーターを読み飛ばす */
p--; /* 行きすぎたぶん */
break;
default:
break;
}
if (eol_flag)
break;
}
}
/*
.ZMS 解析:".o0c = fname.pcm" の場合
返り値
= 0:成功&致命的でないエラー(認識できない.ZMS)
< 0:エラー(.PCMが読み込めない)
*/
static int LoadZmsPcmAssign (char *line_buffer)
{
int track, type;
char temp_fname[94]; /* .CNF に書かれていたファイル名 */
char full_fname[94]; /* フルパスに展開したファイル名 */
FILE *fp2;
if (!isdigit (line_buffer[2]))
return (0);
track = line_buffer[2] - '0';
if (track > 7)
return (0);
type = line_buffer[3] - 'c';
if ((type < 0) || (type > 3))
return (0);
{
unsigned char *p = &line_buffer[4];
unsigned char *q = temp_fname;
int count = 0;
char c;
/* 空白もしくは '=' を読み飛ばす */
while ((c = *p++) && (isspace (c) || (c == '=')));
if (!c) /* いきなり改行されてしまった? */
return (0);
--p;
/* ファイル名をコピー */
while ((c = *q++ = *p++) && (!isspace (c)) && (count++ < 92));
*--q = '\0';
}
/* カレントディレクトリ、環境変数 zmusic,zmusic0~zmusic9 から .PCM ファイルを検索 */
if (!GetPcmPath (temp_fname, full_fname)) {
printf ("ファイル %s が読み込めません\n", temp_fname);
return (-1);
}
fp2 = fopen (full_fname, "rb");
if (fp2 == NULL) {
printf ("ファイル %s が読み込めません\n", temp_fname);
return (-1);
}
strcpy (&pcm_full_fname[type][track][0], temp_fname);
/* 表示用ファイル名を取り出す */
{
char drive[4], dir[94], fname[23], ext[23]; /* パス名分解用 */
_splitpath (full_fname, drive, dir, fname, ext);
strcpy (pcm_fname[type][track], fname);
strcat (pcm_fname[type][track], ext);
}
fseek (fp2, 0, SEEK_END);
pcm_size[type][track] = ftell (fp2);
fseek (fp2, 0, SEEK_SET);
if (pcm_ptr[type][track]) /* 既に確保されていれば */
free (pcm_ptr[type][track]);
pcm_ptr[type][track] = malloc (pcm_size[type][track]);
if (pcm_ptr[type][track] == NULL) {
printf ("PCM 用メモリが足りません\n");
fclose (fp2);
return (-1);
}
fread (pcm_ptr[type][track], pcm_size[type][track], 1, fp2);
fclose (fp2);
return (0);
}
/* .ZMS ファイル読み込み */
int SoundLoadZms (char *fname)
{
FILE *fp;
int track;
#define LINE_BUFFER_SIZE 1024
char line_buffer[LINE_BUFFER_SIZE];
int last_track = -1;
fp = fopen (fname, "r");
if (fp == NULL)
return (-1);
/* .ZMS 解析用ワークの初期化 */
for (track = 0; track < TRACK_MAX; track++) {
zms_analyse.note_no[track] = 0;
zms_analyse.volume_current[track] = 8;
zms_analyse.l_current[track] = 0;
}
/* .ZMSファイル解析・行ループ */
while (fgets (line_buffer, LINE_BUFFER_SIZE, fp)) {
switch (line_buffer[0]) {
case '.':
if ((line_buffer[1] == 'o') || (line_buffer[1] == 'O')) {
if (LoadZmsPcmAssign (line_buffer) < 0) {
fclose (fp);
return (-1);
}
}
break;
case '(':
switch (line_buffer[1]) {
case 't': /* (t9) など */
{
int track;
int ct; /* 何文字目から解析するか */
if (!isdigit (line_buffer[2]))
break;
track = line_buffer[2] - '0';
if (isdigit (line_buffer[3])) {
track = track * 10 + (line_buffer[3] - '0');
if (line_buffer[4] != ')')
break;
ct = 5;
} else {
if (line_buffer[3] != ')')
break;
ct = 4;
}
track -= 9; /* (t9)~(t16) を track0~7 にする */
if ((track >= 0) && (track < 8)) {
LoadZmsTrack (line_buffer, track, ct);
last_track = track;
}
}
break;
default:
break;
}
break;
case '\t': /* (t9) が省略された時 */
if (last_track >= 0)
LoadZmsTrack (line_buffer, last_track, 1);
break;
default:
break;
}
}
fclose (fp);
/* 小節数を求める */
bar_max = 0;
for (track = 0; track < TRACK_MAX; track++) {
int i;
for (i = 0; i < BAR_MAX_TABLE; i++) {
/* 使われている音符数を調べてその最大値に合わせる */
if (zms_analyse.note_no[track] > (bar_max_table[i] * NOTE_DISP)) {
if (bar_max < i + 1)
bar_max = i + 1;
}
}
}
return (0);
}
/* 起動時に1回だけ呼ばれる */
int SoundInit0 (char *fname)
{
int track, type;
for (type = 0; type < TYPE_MAX; type++) {
for (track = 0; track < TRACK_MAX; track++) {
pcm_fname[type][track][0] = '\0';
pcm_ptr[type][track] = NULL;
}
}
return (0);
}
/* PCM を鳴らす */
void SoundPcm8Out (int track, int type, int volume)
{
/* 一応存在するかチェック */
if (pcm_ptr[type][track])
Pcm8Play (track, (volume << 16) + 0x0403,
pcm_ptr[type][track], pcm_size[type][track]);
}
/* 終了時に1回だけ呼ばれる */
void SoundTini0 (void)
{
int track, type;
for (type = 0; type < TYPE_MAX; type++) {
for (track = 0; track < TRACK_MAX; track++) {
if (pcm_ptr[type][track]) /* 既に確保されていれば */
free (pcm_ptr[type][track]);
}
}
}